#include "lib.h"

/*
 * Calcula os primos de 0 ate N
 * parametros:
 * 	primos: vetor de inteiros para saber se o numero e primo ou nao (0 - se nao for primo | 1 - se for primo)
 * 	tamanho: tamanho do vetor (N + 1)
 */
void crivoEratostenes(int *primos, int tamanho){
	int 	cntPrimos, 
		multiplo, /* multiplo do numero primo */
		max = (int)sqrt(tamanho); /* valor limite na verificacao do algoritmo */

	/* incia o vetor com 1 */
	for(cntPrimos = 2; cntPrimos < tamanho; ++cntPrimos){
		primos[cntPrimos] = 1;		
	}

	/* 0 e 1 nao sao primos */
	primos[0] = 0;
	primos[1] = 0;

	/* calcula os primos */
	for(cntPrimos = 2; cntPrimos < max; ++cntPrimos){
		/* se encontra um primo marca todos os seus multiplos como nao primos */		
		if(primos[cntPrimos] == 1){
			for(multiplo = cntPrimos * 2; multiplo < max; multiplo += cntPrimos){
				primos[multiplo] = 0;
			}
		}
	}
}

/*
 * Calcula os primos de 0 ate N
 * parametros:
 * 	primos: vetor de inteiros para saber se o numero e primo ou nao (0 - se nao for primo | 1 - se for primo)
 * 	tamanho: tamanho do vetor (N + 1)
 */
void crivoEratostenesParalelo(int *primos, int tamanho){
	int 	cntPrimos, 
		multiplo, /* multiplo do numero primo */
		max = (int)sqrt(tamanho); /* valor limite na verificacao do algoritmo */

	/* incia o vetor com 1 */
	for(cntPrimos = 2; cntPrimos < tamanho; ++cntPrimos){
		primos[cntPrimos] = 1;		
	}

	/* 0 e 1 nao sao primos */
	primos[0] = 0;
	primos[1] = 0;

	/* calcula os primos */
	for(cntPrimos = 2; cntPrimos < max; ++cntPrimos){
		/* se encontra um primo marca todos os seus multiplos como nao primos */		
		if(primos[cntPrimos] == 1){
			/* cria varias threads para descatarem os multiplos do numero primo */
			#pragma omp parallel shared(primos)
			{
				/* paraleliza a deteccao dos multiplos */
				#pragma omp for schedule(static) private(multiplo)
				for(multiplo = cntPrimos * 2; multiplo < max; multiplo += cntPrimos){
					primos[multiplo] = 0;
				}
			}
		}
	}
}

/*
 * Calcula a soma dos codigos ASCII dos caracteres de uma string
 * parametros:
 *	str: string da qual sera calculada a soma
 * retorno:
 * 	soma dos codigos ASCII
 */
int somaASCII(char *str){
	int i, soma = 0;

	/* soma os codigos ASCII ate encontrar o final da string */
	for(i = 0; str[i] != '\0'; ++i){
		soma += (int)str[i];
	}

	return soma;
}

/*
 * Verifica se uma string e palindromo
 * parametros:
 * 	str: string a ser verificada
 * retorno:
 * 	se a string eh ou nao palindromo
 */
int palindromo(char *str){
	int 	i, 
		len = strlen(str), /* tamanho da string */
		meio = len / 2; /* metade da string (ultimo caractere que deve ser verificado) */

	--len; /* calcula o ultimo indice de caractere da string */

	/* compara os caracteres da string ate o meio ou ate encontrar um caractere diferente */
	for(i = 0; i < meio && str[i] == str[len - i]; ++i);

	/* se as comparacoes ate o meio da string deram certo eh palindromo (retorna verdadeiro) senao retorna falso */
	return(i == meio);
}

/*
 * Converte uma string para minuscula
 * parametros:
 * 	str: string a ser convertida
 */
void minuscula(char *str){
	int i;

	/* percorre a string */
	for(i = 0; str[i] != '\0'; ++i){
		/* se o caractere eh uma letra maiuscula faz a conversao */	
		if(str[i] >= 'A' && str[i] <= 'Z'){
			str[i] += ('a' - 'A');
		}
	}
}

/*
 * Remove caracateres nao alfanumericos de uma string
 * parametros:
 * 	str: string a ser processada
 * retorno:
 * 	se a string eh vazia ou nao
 */
int limpa(char *str){
	int 	i, /* contador para percorrer a string */ 
		limpo; /* comprimento da string limpa */

	limpo = 0;

	/* percorre a string ate o final */
	for(i = 0; str[i] != '\0'; ++i){
		/* se encontrar um caracter alfanumerico coloca na parte limpa da string */
		if((str[i] >= 'a' && str[i] <= 'z') ||
			(str[i] >= 'A' && str[i] <= 'Z') ||
			(str[i] >= '0' && str[i] <= '9')){	
			str[limpo] = str[i];
			++limpo;
		}
	}
	str[limpo] = '\0';

	/* retorna se a string eh vazia ou nao */
	return(limpo != 0);
}

/*
 * Processa uma paticao do texto (divide o texto em palavras, verifica se essas sao palindromos e se a soma dos caracteres dos palindromos eh primo)
 * parametros:
 * 	particao: particao do texto a ser processada
 * 	primos: vetor binario indicando quais numeros sao primos
 * 	nPalindromos: parametro de retorno que contera o numero de palindromos encontrados no texto
 * 	nPrimos: parametro de retorno que contera o numero de palindromos cuja soma eh um numero primo
 */
void processaParticao(char *particao, int *primos, int *nPalindromos, int *nPrimos){
	char 	*token, /* parte da string a ser analisada */ 
		*listaTokens = " \n\t\r", /* lista de tokens para dividir a string */
		*saveptr; /* ponteiro que salva a parte da string que esta sendo processada pelo strtok_r */
	int cntPalindromos = 0, cntPrimos = 0, somaChar;
	
	/* busca o primeiro token */
	token = strtok_r(particao, listaTokens, &saveptr);

	/* continua dividindo a string ate que ela seja totalmente processada */
	while(token != NULL){
		/* remove caracteres nao alfanumericos e verifica se a string final eh nao vazia */
		if(limpa(token)){
			minuscula(token); /* transforma em minuscula */

			/* verifica se a palavra eh palindromo */
			if(palindromo(token)){
				++cntPalindromos;
				somaChar = somaASCII(token); /* soma os valores ASCII dos caracteres */

				/* verifica se a soma eh um numero primo */
				if(primos[somaChar] == 1){
					++cntPrimos;
				}
			}
		}
		token = strtok_r(NULL, listaTokens, &saveptr); /* busca o proximo token */
	}

	//printf("NPALINDROMOS = %d NPRIMOS = %d\n", cntPalindromos, cntPrimos);

	/* define os parametros de retorno */
	(*nPalindromos) = cntPalindromos;
	(*nPrimos) = cntPrimos;
}

/*
 * Processa uma paticao do texto (divide o texto em palavras, verifica se essas sao palindromos e se a soma dos caracteres dos palindromos eh primo)
 * parametros:
 * 	particao: particao do texto a ser processada
 * 	nPalindromos: parametro de retorno que contera o numero de palindromos encontrados no texto
 * 	nPalindromosFrases: parametro de retorno que contera o numero de frases que sao palindromos
 */
void processaParticaoFrases(char *particao, int *nPalindromos, int *nPalindromosFrases){
	char 	*token, /* parte da string a ser analisada */ 
		*listaTokens = " \n\t\r", /* lista de tokens para dividir a particao em palavras */
		*listaTokensFrases = ".!?", /* lista de tokens para dividir a paricao em frases */
		*saveptr, /* ponteiro que salva a parte da string que esta sendo processada pelo strtok_r */
		*copiaParticao; /* copia a paricao para poder detectar frases palindromos */
	int cntPalindromos = 0, cntPalindromosFrases = 0;

	/* aloca e cria uma copia da particao */
	copiaParticao = (char *)malloc(sizeof(char) * (strlen(particao) + 1));	
	strcpy(copiaParticao, particao);

	/* busca o primeiro token */
	token = strtok_r(particao, listaTokens, &saveptr);

	/* continua dividindo a string ate que ela seja totalmente processada */
	while(token != NULL){
		/* remove caracteres nao alfanumericos e verifica se a string final eh nao vazia */
		if(limpa(token)){
			minuscula(token); /* transforma em minuscula */

			/* verifica se a palavra eh palindromo */
			if(palindromo(token)){
				++cntPalindromos;
			}
		}
		token = strtok_r(NULL, listaTokens, &saveptr); /* busca o proximo token */
	}

	/* busca o primeiro token */
	token = strtok_r(copiaParticao, listaTokensFrases, &saveptr);

	/* continua dividindo a string ate que ela seja totalmente processada */
	while(token != NULL){
		/* remove caracteres nao alfanumericos e verifica se a string final eh nao vazia */
		if(limpa(token)){
			minuscula(token); /* transforma em minuscula */

			/* verifica se a palavra eh palindromo */
			if(palindromo(token)){
				++cntPalindromosFrases;
			}
		}
		token = strtok_r(NULL, listaTokensFrases, &saveptr); /* busca o proximo token */
	}

	//printf("NPALINDROMOS = %d NPALINDROMOSFRASES = %d\n", cntPalindromos, cntPalindromosFrases);

	free(copiaParticao);

	/* define os parametros de retorno */
	(*nPalindromos) = cntPalindromos;
	(*nPalindromosFrases) = cntPalindromosFrases;
}


void printPrimos(int *primos, int nPrimos){
	int i;
	
	for(i = 0; i < nPrimos; ++i){
		printf("%d\n", primos[i]);
	}
}

